package org.springframework.boot.netty.handler;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.netty.support.PlaceholderParser;
import org.springframework.boot.netty.handler.common.AbstractChannelInboundHandlerAdapter;
import org.springframework.boot.netty.listener.HandlerMappingAdapter;
import org.springframework.boot.netty.listener.Message;
import org.springframework.boot.netty.support.HttpParamParser;
import org.springframework.boot.netty.support.PathMatchWrapper;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.ResourceUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static io.netty.handler.codec.http.HttpMethod.*;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

/**
 * Author: huoxingzhi
 * Date: 2020/12/22
 * Email: hxz_798561819@163.com
 */
public class HttpDispatcherHandler extends AbstractChannelInboundHandlerAdapter{

    private FullHttpRequest request;
    private Channel channel;

    private final static Logger logger = LoggerFactory.getLogger(HttpDispatcherHandler.class);

    private Map<String,String> mappingWithPlaceHolder = null;

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        if(msg instanceof FullHttpRequest) {
            channel = ctx.channel();
            request = (FullHttpRequest) msg;

            System.out.println(HttpParamParser.parseParams((FullHttpRequest) msg));


            // 2)确保方法是我们需要的(目前只支持GET | POST | PUT | HEAD ,其它不支持)
            if (request.method() != GET && request.method() != POST//
                    && request.method() != PUT && request.method() != HEAD) {
                ReferenceCountUtil.release(msg);
                HttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, METHOD_NOT_ALLOWED);
                ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
                return;
            }

            QueryStringDecoder decoder = new QueryStringDecoder(request.uri());
            String path = decoder.path();

            /**
             * 将占位符替换为"*"通配匹配
             */
            if(mappingWithPlaceHolder==null){
                mappingWithPlaceHolder = new HashMap();
                HandlerMappingAdapter
                        .getPublicHandleMapping()
                        .keySet()
                        .stream()
                        .forEach(mapping ->{
                            mappingWithPlaceHolder.put(PlaceholderParser.parse1(mapping),mapping);
                        });
            }


            String bestMatchMapping = PathMatchWrapper
                    .getBestMath(mappingWithPlaceHolder.keySet(),path);

            if(null == bestMatchMapping
                   ||
                   null == HandlerMappingAdapter
                           .getPublicHandleMapping()
                           .get(mappingWithPlaceHolder.get(bestMatchMapping))){

                // 返回静态页面或404
                ReferenceCountUtil.release(msg);
                FullHttpResponse response = buildFullHttpResponse(String.format("static%s", path));
                ctx.channel().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
                return;
            }

            Message message = new Message();
            message.setContent(msg);
            /**
             * 获取原生的占位符，后期 @PathVariable使用
             */
            message.setMappedURI(mappingWithPlaceHolder.get(bestMatchMapping));
            message.setHttpURI(path);

            /**
             * 解析Authorization
             * Authorization=bearer0206e73d-3e2b-4886-90ff-1e7edc6b34f5
             */
            Map<String, String> parsedHeaders = HttpParamParser.parseHeaders(request);
            if(parsedHeaders.containsKey("Authorization")){
                String accessToken = StringUtils.substringAfter(
                        String.valueOf(parsedHeaders.get("Authorization")),
                        "bearer"
                );
                message.setAccessToken(accessToken);
            }

            /*
            request 后续参数解析器还需要使用，请求完成后再释放资源
            ReferenceCountUtil.release(msg);
            */
            super.channelRead(ctx,message);
        }else {
            // 不能处理，交给其他handler
            ctx.fireChannelRead(msg);
        }
    }

    private ByteBuf copiedBuffer(String data, Charset utf8) {

        return Unpooled.wrappedBuffer(data.getBytes());
    }

    // 创建数据流
    private FullHttpResponse buildFullHttpResponse(String resPath) {

        try {
            File file = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX+resPath.trim());
            InputStream inputStream=new FileInputStream(file);
            if (inputStream != null) {
                int available = inputStream.available();
                if (available != 0) {
                    byte[] bytes = new byte[available];
                    inputStream.read(bytes);
                    ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(bytes.length).writeBytes(bytes);
                    inputStream.close();
                    return new DefaultFullHttpResponse(HTTP_1_1, OK, byteBuf.retainedDuplicate());
                }
            }
        } catch (Exception e){
            logger.debug(e.getMessage());
        }

        return new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND,buildDefaultForbiddenByteBuf());
    }


    private ByteBuf buildDefaultForbiddenByteBuf(){
        String response = "<!DOCTYPE html>\n" +
                "<html>\n" +
                "<head>\n" +
                "    <title>You can't do that (404)</title>\n" +
                "    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n" +
                "    <style type=\"text/css\">\n" +
                "        body { background-color: #fff; color: #666; text-align: center; font-weight:bold ; font-family: Arial, \"Times New Roman\", \"楷体\" ; }\n" +
                "        div.dialog {\n" +
                "            width: 25em;\n" +
                "            padding: 0 4em;\n" +
                "            margin: 4em auto 0 auto;\n" +
                "            border: 1px solid #ccc;\n" +
                "            border-right-color: #999;\n" +
                "            border-bottom-color: #999;\n" +
                "        }\n" +
                "        h1 { font-size: 400%; color: #f00; line-height: 1em; }\n" +
                "    </style>\n" +
                "</head>\n" +
                "\n" +
                "<body>\n" +
                "<div class=\"dialog\">\n" +
                "    <h1>404</h1>\n" +
                "    <p>That Resource Not Found!</p>\n" +
                "</div>\n" +
                "</body>\n" +
                "</html>";
        return copiedBuffer(response, CharsetUtil.UTF_8);
    }

}
